package tsb.trinhnx.main;

/**
 * @author q40118f5
 *	Our idea is store all as a char[] array :D
 *	Implement all bunch things based on that.
 *	Note, we have to handle the negative value with -
 *	Note2: A mark bit to indicate the positive / negative ??
 *	Add one more boolean value
 *	Convention: 0 has positive (+)
 *	We will not accept sth like --121, or ++1212 or -+1232 :D
 */
public class BigInteger {
	// Contains all valid data	
	private final char[] VALID_VALUES = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
	// The data?, using enumaration :D may be better :/
	private final char[] VALID_ANNOTATION = { '-', '+' };
	private char[] bigInteger;
	private boolean isPositive;

	// Create static variable ZERO and ONE
	public static final BigInteger ZERO = new BigInteger(0L);
	public static final BigInteger ONE = new BigInteger(1L);

	// Create default is 0 :D
	public BigInteger() {
		bigInteger = new char[1];
		isPositive = true;
		bigInteger[0] = 0;
	}

	// Create BigInt base on the string
	public BigInteger(final String input) {
		/**
		 * 1. Check validation input 
		 * 	(only contains - at position 0 - first,
		 * 	[0...9] - number value)
		 * 2. If not valid, throws Exception
		 * 		else create new one
		 */

		// 1. Validation
		// Null one
		if (input == null) {
			throw new IllegalArgumentException("Null input");
		}
		// Remove head/tail space
		String trim = input.trim();
		int length = trim.length();
		// Empty one
		if (length == 0) {
			// Return empty one,0 :D
			bigInteger = new char[1];
			isPositive = true;
			bigInteger[0] = '0';
		} else {
			// Processing
			// If string contains - , it must be at the first, otherwise, exception
			// If string contains non invalid data, exception
			// :( I am so bad at regex :(. do it manually
			bigInteger = new char[length];
			// Do compare each character with our valid array
			// If match, use it.
			char[] c_input = trim.toCharArray();
			for (int i = 0; i < length; i++) {
				if (i == 0) {
					// If use annotation, then no need check 0 index
					// Start check from 1 index
					if (isAnnotation(c_input) != 0) {
						bigInteger[i] = '0';
						// Create new array char that eliminate 1 element
						i = i + 1;
					}
				}

				/**
				 * If flag = true, then this character is ok, copy it
				 * Else raise error
				 */
				if (isDigit(c_input[i])) {
					// Index out exception if only - :/
					bigInteger[i] = c_input[i];
				} else {
					throw new IllegalArgumentException("Invalid character: " + c_input[i]);
				}
			}
		}
	}

	public BigInteger(final long input) {
		// Return this using the string :D
		// It seems over-head, but, my lazy again :D
		this(String.valueOf(input));
	}

	public char[] getBigInteger() {
		return bigInteger;
	}

	public boolean getPositive() {
		return isPositive;
	}

	public void setPositive(boolean isPositive) {
		this.isPositive = isPositive;
	}

	/**
	 * Add another big integer with this object
	 * @param another
	 * @return another big integer
	 * Our idea is like the primary school student :D
	 * Add one by one.
	 * NOTE: We just handle two pure Positive number :D
	 * In another case, move to subtract or using reserve :D
	 */
	public BigInteger addBigInteger(final BigInteger another) {
		BigInteger firstNumber, secondNumber;
		int padding; // Number of zero to pad to make same length
		boolean is_positive = false; // The flag indicates the mask of result (+ or -)
		if (this.getPositive()) {
			// 2 + 6
			if (another.getPositive()) {
				is_positive = true;
				padding = this.getBigInteger().length - another.getBigInteger().length;
				if (padding >= 0) {
					firstNumber = this;
					secondNumber = another.paddingZero(padding, 1);
				} else {
					firstNumber = this.paddingZero(-padding, 1);
					secondNumber = another;
				}
			} else {
				// 2 + -6 = 2  - +6
				// Reserve another one.
				another.setPositive(true);
				return this.subtractBigInteger(another);
			}
		} else {

			if (another.getPositive()) {
				// -2 + 6 = 6 - +2
				this.setPositive(true);
				return another.subtractBigInteger(this);
			} else {
				is_positive = false;
				// -2 + -6 = -(6 + 2)
				padding = this.getBigInteger().length - another.getBigInteger().length;
				if (padding >= 0) {
					firstNumber = this;
					secondNumber = another.paddingZero(padding, 1);
				} else {
					firstNumber = this.paddingZero(-padding, 1);
					secondNumber = another;
				}
			}
		}
		/**
		 * A + B 1 
		 * A + -B = A - B 
		 * -A + -B = -(A + B) 3
		 * -A + B  = B -A 4
		 */
		// Ok, same length
		char[] first_number = firstNumber.getBigInteger();
		char[] second_number = secondNumber.getBigInteger();
		StringBuilder sb = new StringBuilder(1);
		int divisior = 0;
		for (int i = first_number.length - 1; i >= 0; i--) {
			// Convert to integer
			int temp1 = Integer.valueOf(String.valueOf(first_number[i]));
			int temp2 = Integer.valueOf(String.valueOf(second_number[i]));
			int value = (divisior + temp1 + temp2) % 10;
			// add to stringbuilder
			sb.append(value);
			divisior = (divisior + temp1 + temp2) / 10;
		}
		if (divisior != 0) {
			sb.append(divisior);
		}
		if (!is_positive) {
			sb.append('-');
		}
		return new BigInteger(sb.reverse().toString()).trimmingZero();
	}

	/**
	 * Subtract this object with another one
	 * @param another
	 * @return
	 */
	public BigInteger subtractBigInteger(final BigInteger another) {

		/**
		 * A-B 1 
		 * A-(-B) = A + B 2 
		 * -A - B = -(A + B) 3
		 * -A - (-B) = B -A 4
		 */
		// First number is the one has absolute value is bigger
		// Note that, we do not deal with 1 or 3 :D 
		BigInteger firstNumber, secondNumber;
		int padding; // Number of zero to pad to make same length
		boolean is_negative = false; // The flag indicates the mask of result (+ or -)
		if (this.getPositive()) {
			if (another.getPositive()) {
				System.out.println("nothing to do 1 :D");
				if (this.compareTo(another) >= 0) {
					// This one is bigger than another and not negative
					// 6-2
					padding = this.getBigInteger().length - another.getBigInteger().length;
					firstNumber = this;
					secondNumber = another.paddingZero(padding, 1);
				} else {
					// 2-6 = -(6-2)
					padding = another.getBigInteger().length - this.getBigInteger().length;
					firstNumber = another;
					secondNumber = this.paddingZero(padding, 1);
					is_negative = true;
				}
			} else {
				// Add -B
				// Reserve another one.
				another.setPositive(true);
				return this.addBigInteger(another);
			}
		} else {
			if (another.getPositive()) {
				// -2 -6 = - (2 +6)
				another.setPositive(false);
				return this.addBigInteger(another);
			} else {
				System.out.println("nothing to do 2 :D");
				if (this.compareTo(another) >= 0) {
					// This one is bigger than another and not negative
					// -2 -(-6) = 6 - 2
					padding = another.getBigInteger().length - this.getBigInteger().length;
					firstNumber = another;
					secondNumber = this.paddingZero(padding, 1);
					is_negative = false;

				} else {
					// This one is smaller than another and negative
					// - 6 - (-2) = - (6-2)
					is_negative = false;
					padding = this.getBigInteger().length - another.getBigInteger().length;
					firstNumber = this;
					secondNumber = another.paddingZero(padding, 1);
				}
			}
		}

		// Ok, same length
		char[] first_number = firstNumber.getBigInteger();
		char[] second_number = secondNumber.getBigInteger();

		StringBuilder sb = new StringBuilder(1);
		int carry = 0;
		for (int i = first_number.length - 1; i >= 0; i--) {
			// Convert to integer
			int temp1 = Integer.valueOf(String.valueOf(first_number[i]));
			int temp2 = Integer.valueOf(String.valueOf(second_number[i]));
			int value;
			if (temp1 < temp2) {
				value = (10 + temp1 - temp2 - carry) % 10;
				carry = 1;
			} else {
				value = (temp1 - temp2 - carry) % 10;
				carry = 0;
			}
			// add to stringbuilder
			sb.append(value);
		}
		System.out.println("Subtract: " + sb.toString());
		// Ok, check the flag to add - or +
		if (is_negative)
			sb.append('-');
		return new BigInteger(sb.reverse().toString()).trimmingZero();
	}

	@Override
	public boolean equals(Object obj) {
		return false;
	}

	public BigInteger clone() {
		return null;
	}

	/**
	 * Compare this with another one
	 * 	Compare using 2 factors
	 * 	1. The values itself
	 * 	2. The value (is positive or negative :D)
	 * 		Trim first
	 * 		Then compare
	 * @param another
	 * @return -1 if less, 0 if equal and 1 if bigger
	 */
	public int compareTo(final BigInteger another) {
		// Flag for value itself
		int flag_value = 0;
		int padding = this.getBigInteger().length - another.getBigInteger().length;
		/**
		 *	1. Padding
		 *	2. Adding
		 */
		BigInteger firstNumber, secondNumber;
		if (padding > 0) {
			firstNumber = this;
			secondNumber = another.paddingZero(padding, 1);
		} else {
			firstNumber = this.paddingZero(-padding, 1);
			secondNumber = another;
		}
		char[] first_number = firstNumber.getBigInteger();
		char[] second_number = secondNumber.getBigInteger();
		/**
		 * It is longer, it is bigger
		 */
		if (first_number.length > second_number.length) {
			flag_value = 1;
		} else if (first_number.length == second_number.length) {
			for (int i = 0; i < second_number.length; i++) {
				// Convert to integer
				int temp1 = Integer.valueOf(String.valueOf(first_number[i]));
				int temp2 = Integer.valueOf(String.valueOf(second_number[i]));
				if (temp1 > temp2) {
					flag_value = 1;
					break;
				} else if (temp1 < temp2) {
					flag_value = -1;
					break;
				}
				// If the same, ok, 0
				flag_value = 0;
			}
		} else {
			flag_value = -1;
		}

		// Now we have the flag_value
		// At this point we will compare the annotation
		System.out.println("Flag: " + flag_value);
		boolean this_flag = this.getPositive();
		boolean another_flag = another.getPositive();
		/**
		 * 1. If value is bigger
		 * 	- If flag is + -> 1
		 * 	- If flag is - -> -1
		 * 2. If equal
		 * 	- same value, then 0
		 *  - true / false: 1
		 *  - false / true: -1
		 * 3. If value is smaller
		 * 	- If another flag is +, smaller -1
		 * 	- Else +1
		 */

		if (flag_value > 0) {
			if (this_flag) {
				flag_value = 1;
			} else {
				flag_value = -1;
			}

		} else if (flag_value == 0) {
			if (this_flag) {
				if (another_flag) {
					flag_value = 0;
				} else {
					flag_value = 1;
				}
			} else {
				if (another_flag) {
					flag_value = -1;
				} else {
					flag_value = 0;
				}
			}

		} else {
			if (another_flag) {
				flag_value = -1;
			} else {
				flag_value = 1;
			}
		}

		return flag_value;
	}

	public long toLong() {
		return 0;
	}

	@Override
	public String toString() {
		// Printout this :D
		StringBuilder sb = new StringBuilder(1);
		sb.append(isPositive ? ' ' : '-');
		for (int i = 0; i < bigInteger.length; i++) {
			sb.append(bigInteger[i]);
		}
		return sb.toString();
	}

	/**
	 * Padding number zero at position
	 * @param input
	 * @param numberPadding : number 0 to add (>= 0)
	 * @param position : add at the head (1) or tail (0) : ENUMMMMMMm
	 * @return new big number( seem confused :/)
	 * 
	 */
	public BigInteger paddingZero(int numberPadding, int position) {
		if (numberPadding < 0) {
			throw new IllegalArgumentException(" Not valid padding: " + numberPadding);
		} else if (numberPadding == 0) {
			return this;
		} else {
			BigInteger number;
			StringBuilder sb;
			switch (position) {
			case 0:
				sb = new StringBuilder(1);
				sb.append(this.getBigInteger());
				for (int i = 0; i < numberPadding; i++) {
					sb.append('0');
				}
				number = new BigInteger(sb.toString());
				break;
			case 1:
				sb = new StringBuilder(1);
				for (int i = 0; i < numberPadding; i++) {
					sb.append('0');
				}
				sb.append(this.getBigInteger());
				number = new BigInteger(sb.toString());
				break;
			default:
				throw new IllegalArgumentException(" Not valid position: " + position);
			}
			number.setPositive(this.getPositive());
			return number;
		}
	}

	/**
	 * Create new number that is resevered with this
	 * @return
	 */
	public BigInteger reverse() {
		BigInteger newInteger = new BigInteger(this.toString());
		newInteger.setPositive(!this.getPositive());
		return newInteger;
	}

	/**
	 * Remove the heading zero
	 * @return
	 */
	public BigInteger trimmingZero() {
		char[] this_number = this.getBigInteger();
		StringBuilder sb = new StringBuilder(1);
		final int length = this_number.length;
		int ignore_position = length;
		for (int i = 0; i < length; i++) {
			if (this_number[i] != '0') {
				ignore_position = i;
				break;
			}
		}
		for (int i = ignore_position; i < length; i++) {
			sb.append(this_number[i]);
		}
		BigInteger bigNumber = new BigInteger(sb.toString());
		bigNumber.setPositive(this.getPositive());
		return bigNumber;
	}

	/**
	 * Check a char is digit or not
	 * @param c
	 * @return
	 */
	private boolean isDigit(final char c) {
		boolean isDigit = false; // indicate this character is right or wrong
		for (int i = 0; i < VALID_VALUES.length; i++) {
			if (VALID_VALUES[i] == c) {
				isDigit = true;
				break;
			}
		}
		return isDigit;
	}

	/**
	 * Check that char array uses - or + or not
	 * @param input
	 * @return -1 if -, +1 if + and 0 if not use
	 */
	private int isAnnotation(final char[] c) {
		int result;
		// NOTE: I do not check the length or null :D
		if (VALID_ANNOTATION[0] == c[0]) {
			result = -1;
			isPositive = false;
		} else if (VALID_ANNOTATION[1] == c[0]) {
			result = 1;
			isPositive = true;
		} else {
			result = 0;
			isPositive = true;
		}
		return result;
	}

}
